package com.example.logging.aspect;

import com.example.logging.annotation.*;
import com.example.logging.entity.*;
import com.example.logging.repository.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.*;

import java.time.LocalDateTime;
import java.util.Arrays;

@Aspect
@Component
@RequiredArgsConstructor
public class LogAspect {
    
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
    
    private final OperationLogRepository operationLogRepository;
    private final ExceptionLogRepository exceptionLogRepository;
    
    @Pointcut("@annotation(com.example.logging.annotation.OperationLog)")
    public void operationLogPointcut() {}
    
    @Around("operationLogPointcut()")
    public Object logOperation(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request =
            ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        
        String method = joinPoint.getSignature().toShortString();
        String args = Arrays.toString(joinPoint.getArgs());
        String uri = request.getRequestURI();
        String ip = request.getRemoteAddr();
        LocalDateTime timestamp = LocalDateTime.now();
        
        try {
            Object result = joinPoint.proceed();
            
            log.info("[Operation] URI: {} | Method: {} | IP: {} | Args: {} | Result: {}", uri, method, ip, args, result);
            
            OperationLogEntity opLog = new OperationLogEntity();
            opLog.setUri(uri);
            opLog.setMethod(method);
            opLog.setIp(ip);
            opLog.setArgs(args);
            opLog.setResult(String.valueOf(result));
            opLog.setStatus("SUCCESS");
            opLog.setTimestamp(timestamp);
            
            operationLogRepository.save(opLog);
            
            return result;
        } catch (Exception e) {
            log.error("[Exception] Method: {} | Args: {} | Error: {}", method, args, e.getMessage(), e);
            
            ExceptionLogEntity exLog = new ExceptionLogEntity();
            exLog.setUri(uri);
            exLog.setMethod(method);
            exLog.setIp(ip);
            exLog.setArgs(args);
            exLog.setExceptionType(e.getClass().getName());
            exLog.setExceptionMessage(e.getMessage());
            exLog.setStackTrace(Arrays.toString(e.getStackTrace()));
            exLog.setTimestamp(timestamp);
            
            exceptionLogRepository.save(exLog);
            
            throw e;
        }
    }
}
